/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <boost/scoped_ptr.hpp>

#include <noise/Manager.h>
#include <noise/hard/Channel.h>
#include <math/Vector3.h>
#include <SharedRep.h>
#include <Atom.h>
#include <list>
#include <deque>
#ifdef MAC_VERSION
#include <openal/al.h>
#else
#include <al.h> 
#endif

namespace liba {
namespace noise {
namespace hard {
namespace openal {
class Hardware;

struct WAV
{
	unsigned short wFormatTag;
	unsigned short nChannels;
	unsigned nSamplesPerSec;
	unsigned nAvgBytesPerSec;
	unsigned short nBlockAlign;
	unsigned short wBitsPerSample;
};

class ChannelStaticRepName
{
public:
	Atom channel_type;
	filesystem::String name;
	Hardware * hardware;

	ChannelStaticRepName(Hardware * hardware, const Atom & channel_type, const filesystem::String & name):
		hardware( hardware ),
		channel_type( channel_type ),
		name( name )
	{}
	bool operator<(const ChannelStaticRepName & na)const
	{
		if( hardware < na.hardware )
			return true;
		if( hardware > na.hardware )
			return false;
		if( channel_type < na.channel_type )
			return true;
		if( channel_type > na.channel_type )
			return false;
		return name < na.name;
	}
};

class ChannelStaticRep
{
//BUG
public:
	WAV wfx;
	char * mem;
	unsigned size;
	boost::scoped_ptr<DataSource> source;
	
	ALuint buffer;
	ALuint alsource;
	float owned_by_distance;	//only nearest sound will play
public:
	ChannelStaticRep(const ChannelStaticRepName & name);
	~ChannelStaticRep();
	int get_frequency()const
	{
		return wfx.nSamplesPerSec;
	}
	int get_block_align()const
	{
		return wfx.nBlockAlign;
	}
	int get_size()const
	{
		return size;
	}
	ALuint get_buffer()const
	{
		return buffer;
	}
	ALuint get_source()const
	{
		return alsource;
	}
	float get_owned_distance()const
	{
		return owned_by_distance;
	}
	void set_owned_distance(float dst)
	{
		owned_by_distance = dst;
	}
};

class Channel : public hard::Channel
{
public:
	Channel();
	~Channel();
protected:
	properties::DependencyProperty<bool> loop;
	properties::DependencyProperty<double> exact_volume;
	properties::DependencyProperty<double> min_distance, max_distance;
	properties::DependencyProperty<double> exact_pan;
	properties::DependencyProperty<math::Vector3<float> > exact_position;
	properties::DependencyProperty<math::Vector3<float> > exact_speed;
};

class ChannelStatic :
	public Shared<ChannelStaticRepName, ChannelStaticRep>,
	public Channel
{
	Atom channel_type;
	unsigned position;
	double approx_vol;
	bool finished;
	bool in_existance_flag;
public:
	static bool SortPredicate(ChannelStatic * a, ChannelStatic * b)
	{
		return a->approx_vol > b->approx_vol;
	}
	ChannelStatic(Hardware * ha, const Atom & channel_type, const filesystem::String & name);
	virtual ~ChannelStatic();
	void life_cycle(double delta_time, const math::Vector3<float> & listener_position);
	bool in_existance()const
	{
		return in_existance_flag;
	}
	void come_to_existance();
	void begone();
	virtual void play(DataSource * source);
	virtual void stop();
	virtual bool is_finished();
	void on_hardware_delete();
};

class ChannelStreaming : public Channel
{
public:
	ChannelStreaming(Hardware * hardware, const Atom & channel_type, double length_sec);
	virtual ~ChannelStreaming();

	virtual void play(DataSource * source);
	virtual void stop();
	virtual bool is_finished();

	void life_cycle(double delta_time);
	void on_hardware_delete();
private:
	Hardware * hardware;
	std::deque<DataSource *> sources;
	void destroy_sources();
	bool update( ALuint buffer_id );

	ALuint source;
	Atom channel_type;
	unsigned size;
	unsigned minimum_update_size;
	unsigned garbage_position;
	unsigned silence_size;
	bool playing;

	enum
	{
		BSIZE = 4608*2*8*4,
        NUMBUFFERS = 4
	};
	ALbyte temp_data[ BSIZE ];
	ALuint buffers[NUMBUFFERS];
	int buffers_in_queue;

	WAV wfx;
};


} // namespace openal
} // namespace hard
} // namespace noise
} // namesapce liba
using namespace liba;

